#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QLabel>
#include <QUndoStack>
#include <QPointer>
#include <QMessageBox>
#include <QApplication>
#include <QKeyEvent>
#include <QClipboard>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QTextEdit>
#include <QFileDialog>

#include <osgDB/WriteFile>

#include "ExportOSGDlg.h"
#include "Registry.h"
#include "ActorTable.h"
#include "Scene.h"
#include "QtOSGWidget.h"
#include "WidgetUtil.h"

class ExportOSGDlgPrivate
{
public:
	ExportOSGDlgPrivate()
	{}
	bool exportCurve2 = false;
	bool exportCurve3 = false;
	bool exportSurface = true;
	QString fileName;
};

ExportOSGDlg::ExportOSGDlg(QWidget * parent, Qt::WindowFlags f)
	: DlgBase(parent, f)
	, d_ptr(new ExportOSGDlgPrivate)
{
	setWindowTitle(tr("导出OSG文件"));

	auto vBox = new QVBoxLayout(this);
	auto checkbox = createCheckBox(tr("导出二维曲线"), "curve2");
	vBox->addWidget(checkbox);
	checkbox->setChecked(d_ptr->exportCurve2);

	checkbox = createCheckBox(tr("导出三维曲线"), "curve3");
	vBox->addWidget(checkbox);
	checkbox->setChecked(d_ptr->exportCurve3);

	checkbox = createCheckBox(tr("导出曲面"), "surface");
	vBox->addWidget(checkbox);
	checkbox->setChecked(d_ptr->exportSurface);

	auto hbox4 = new QHBoxLayout;
	auto lineEdit = new QLineEdit;
	lineEdit->setObjectName("file");
	lineEdit->setPlaceholderText(tr("保存文件路径"));
	hbox4->addWidget(lineEdit);
	auto button = new QPushButton("...");
	hbox4->addWidget(button);
	QObject::connect(button, &QPushButton::clicked, this, &ExportOSGDlg::getFileName);
	vBox->addLayout(hbox4);

	auto hBox = new QHBoxLayout(this);
	vBox->addLayout(hBox);
	hBox->addStretch(1);
	QPushButton* applyBtn = new QPushButton(this);
	applyBtn->setObjectName("apply");
	applyBtn->setText(tr("导出"));
	connect(applyBtn, &QPushButton::clicked, this, &ExportOSGDlg::exportOSG);
	hBox->addWidget(applyBtn);
	QPushButton* cancelBtn = new QPushButton(this);
	cancelBtn->setText(tr("取消"));
	connect(cancelBtn, &QPushButton::clicked, this, &ExportOSGDlg::close);
	hBox->addWidget(cancelBtn);
	// 让竖向拉伸没有意义
	vBox->addStretch(1);

	setLayout(vBox);

	setMinimumWidth(500);
}

ExportOSGDlg::~ExportOSGDlg()
{
}

void ExportOSGDlg::fromUI()
{
	d_ptr->exportCurve2 = isCheckBoxChecked(this, "curve2", Qt::FindChildrenRecursively);
	d_ptr->exportCurve3 = isCheckBoxChecked(this, "curve3", Qt::FindChildrenRecursively);
	d_ptr->exportSurface = isCheckBoxChecked(this, "surface", Qt::FindChildrenRecursively);
	d_ptr->fileName = getLineEditText(this, "file", Qt::FindChildrenRecursively);
}

void ExportOSGDlg::getFileName()
{
	static QString s_lastDir;
	auto fileName = QFileDialog::getSaveFileName(this, tr("保存osg文件"), s_lastDir, tr("OSG (*.osgt *.osgb)"));
	if (fileName.isEmpty())
		return;
	s_lastDir = QFileInfo(fileName).absolutePath();
	d_ptr->fileName = fileName;
	auto lineEdit = findChild<QLineEdit*>("file", Qt::FindChildrenRecursively);
	if (lineEdit)
		lineEdit->setText(fileName);
}

struct ParentNodeInfo
{
	osg::ref_ptr <osg::Group> parent;
	osg::ref_ptr <osg::Node> node;
	unsigned int index = 0;
};

class RemoveNodesByMaskVisitor : public osg::NodeVisitor
{
	std::vector<ParentNodeInfo>& m_removedNodesInfo;
	unsigned int m_noExportMask = 0;
public:
	RemoveNodesByMaskVisitor(std::vector<ParentNodeInfo>& removedNodesInfo,
		unsigned int noExportMask)
		: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
		, m_removedNodesInfo(removedNodesInfo)
		, m_noExportMask(noExportMask)
	{
	}

	bool shouldRemove(const osg::Node& node)
	{
		auto mask = node.getNodeMask();
		if (mask == NoPickMask)
			return true;
		if ((mask & (D2CurveMask & m_noExportMask)) && (mask & D3SurfaceMask) == 0)
			return  true;
		if ((mask & (D3CurveMask & m_noExportMask)) && (mask & D3SurfaceMask) == 0)
			return  true;
		if ((mask & (D3SurfaceMask & m_noExportMask)) && (mask & D3CurveMask) == 0)
			return  true;
		return false;
	}

	virtual void apply(osg::Node& node) override
	{
		if (shouldRemove(node))
		{
			auto parent = node.getParent(0);
			auto index = parent->getChildIndex(&node);
			m_removedNodesInfo.push_back({parent, &node, index });
		}
		traverse(node);
	}

	virtual void apply(osg::Drawable& drawable) override
	{
		apply(static_cast<osg::Node&>(drawable));
	}
};

void ExportOSGDlg::exportOSG()
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return;

	fromUI();
	if (!d_ptr->fileName.endsWith(".osgt") && !d_ptr->fileName.endsWith(".osgb"))
	{
		QMessageBox::warning(this, tr("三维建模程序"),
			tr("请指定合适后缀名的文件"),
			QMessageBox::Ok);
		return;
	}
	unsigned int noExportMask = 0;
	//if (!d_ptr->exportCurve2)
	//	noExportMask |= D2CurveMask | D2LineMask;
	if (!d_ptr->exportCurve3)
		noExportMask |= D3CurveMask | D3LineMask;
	if (!d_ptr->exportSurface)
		noExportMask |= D3SurfaceMask;
	std::vector<ParentNodeInfo> removedNodesInfo;
	osg::ref_ptr<RemoveNodesByMaskVisitor> visitor = new RemoveNodesByMaskVisitor(removedNodesInfo, noExportMask);

	auto widget3D = reg.get<QtOSGWidget>(widget3DID);
	widget3D->pauseRenderThread();

	auto ucses = scene->getUCS();
	if (!d_ptr->exportCurve2)
	{
		for (int i=0; i<ucses.size(); ++i)
		{
			scene->removeChild(ucses[i]);
		}
	}
	auto osgNode = scene->getRep3D();
	osgNode->accept(*visitor);
	for (const auto& info : removedNodesInfo)
	{
		info.parent->removeChild(info.node);
	}

	osgDB::writeNodeFile(*osgNode, d_ptr->fileName.toLocal8Bit().data());

	if (!d_ptr->exportCurve2)
	{
		for (int i = 0; i < ucses.size(); ++i)
		{
			scene->addChild(ucses[i]);
		}
	}
	for (const auto& info : removedNodesInfo)
	{
		info.parent->insertChild(info.index, info.node);
	}

	widget3D->resumeRenderThread();

	close();
}
